package com.shm.leetcode;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * 841. 钥匙和房间
 * 有 N 个房间，开始时你位于 0 号房间。每个房间有不同的号码：0，1，2，...，N-1，并且房间里可能有一些钥匙能使你进入下一个房间。
 *
 * 在形式上，对于每个房间 i 都有一个钥匙列表 rooms[i]，每个钥匙 rooms[i][j] 由 [0,1，...，N-1] 中的一个整数表示，其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。
 *
 * 最初，除 0 号房间外的其余所有房间都被锁住。
 *
 * 你可以自由地在房间之间来回走动。
 *
 * 如果能进入每个房间返回 true，否则返回 false。
 *
 * 示例 1：
 *
 * 输入: [[1],[2],[3],[]]
 * 输出: true
 * 解释:
 * 我们从 0 号房间开始，拿到钥匙 1。
 * 之后我们去 1 号房间，拿到钥匙 2。
 * 然后我们去 2 号房间，拿到钥匙 3。
 * 最后我们去了 3 号房间。
 * 由于我们能够进入每个房间，我们返回 true。
 * 示例 2：
 *
 * 输入：[[1,3],[3,0,1],[2],[0]]
 * 输出：false
 * 解释：我们不能进入 2 号房间。
 * 提示：
 *
 * 1 <= rooms.length <= 1000
 * 0 <= rooms[i].length <= 1000
 * 所有房间中的钥匙数量总计不超过 3000。
 * @author SHM
 */
public class CanVisitAllRooms {
    /**
     * 示例1就可以访问所有的房间，因为通过房间里的key将房间连在了一起。
     *
     * 示例2中，就不能访问所有房间，从图中就可以看出，房间2是一个孤岛，我们从0出发，无论怎么遍历，都访问不到房间2。
     *
     * 认清本质问题之后，就知道孤岛问题，使用 广度优先搜索(bfs) 还是 深度优先搜索(dfs) 都是可以的。
     *
     * 作者：carlsun-2
     * 链接：https://leetcode-cn.com/problems/keys-and-rooms/solution/841-yao-chi-he-fang-jian-ben-zhi-shi-gu-dao-wen-ti/
     * @param rooms
     * @return
     */
    int num = 0;

    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        return bfs(rooms);
    }

    public boolean bfs(List<List<Integer>> rooms){
        int n = rooms.size();
        boolean[] visited = new boolean[n];
        Queue<Integer> queue = new LinkedList<>();
        visited[0] = true;
        queue.offer(0);
        num++;
        while (!queue.isEmpty()){
            Integer i = queue.poll();
            for (Integer integer : rooms.get(i)) {
                if (!visited[integer]){
                    queue.offer(integer);
                    visited[integer] = true;
                    num++;
                }
            }
        }

//        for (boolean b : visited) {
//            if (!b){
//                return false;
//            }
//        }
//        return true;

        return num==n;
    }

    public boolean canVisitAllRooms_2(List<List<Integer>> rooms) {
        int n = rooms.size();
        boolean[] visited = new boolean[n];
        dfs(0,rooms,visited);
//        for (boolean b : visited) {
//            if (!b){
//                return false;
//            }
//        }
//        return true;
        return num == n;
    }

    public void dfs(int key,List<List<Integer>> rooms,boolean[] visited){
        if (visited[key]){
            return;
        }
        visited[key]=true;
        num++;
        for (Integer integer : rooms.get(key)) {
            dfs(integer,rooms,visited);
        }
    }
}
